perm filename TELNET.MID[NET,MRC]1 blob sn#324540 filedate 1977-12-19 generic text, type C, neo UTF8
COMMENT ⊗   VALID 00025 PAGES
C REC  PAGE   DESCRIPTION
C00001 00001
C00003 00002	TITLE TELNET
C00006 00003	Data area
C00009 00004	TELNET protocol codes
C00012 00005	Interrupt server
C00014 00006	Start of program
C00016 00007	 Check for net-hoppers.
C00018 00008	Top level
C00021 00009	Process host specification
C00024 00010	 Host name specified, ask magical routine to find it
C00026 00011	ICP ICP ICP
C00028 00012	Main program loop
C00030 00013	 TTY input interrupt
C00033 00014	 Network input interrupt
C00036 00015	IAC service
C00038 00016	 IAC WILL/WONT
C00040 00017	 IAC DO/DONT
C00041 00018	Character commands
C00043 00019	Command service routines
C00044 00020	 Append file hacking
C00047 00021	 Output file hacking
C00049 00022	 Input file hacking
C00051 00023	 More commands
C00053 00024	Random subroutines
C00055 00025	 More random subroutines
C00058 ENDMK
C⊗;
TITLE TELNET
SUBTTL Definitions

; Mark Crispin, SU-AI, December 1977

; Assembly switches

IFNDEF NPRSKT,NPRSKT==27		; default ICP socket
IFNDEF HSTNLN,HSTNLN==10.		; host name buffer length
IFNDEF PDLLEN,PDLLEN==50.		; PDL length
IFNDEF TTOBFL,TTOBFL==50.		; TTY output buffer length
IFNDEF CLKSPD,CLKSPD==2.		; number of seconds between clock ints

;  AC definitions.  0→3 (and, at HSTNAM, 4→11) are used by NETWRK.
; 0 is also used as very temp in the main program.
; I is used only at interrupt level.
; X, Y, Z, A, and B are in approximate descending order of usage.

X=4 ? Y=5 ? Z=6 ? A=7 ? B=10 ? I=16 ? P=17

; I/O channels.  NETWRK uses 0 and 1.

DSI==2 ? DSO==3

; Macro to send a TELNET command

DEFINE TELCMD CMDLST
 SKIPE DEBUGP
  OUTSTR [ASCIZ/⊗!CMDLST!*
/]
 IRPS CMD,,CMDLST
  MOVEI CMD
  PUSHJ 17,NETOCH
 TERMIN
 PUSHJ 17,NETSND
TERMIN

; SAIL system bit definitions

INTTTY==020000,,			; TTY input interrupt
INTCLK==000200,,			; clock interrupt
DISLIN==400000,,			; III
DMLIN== 040000,,			; DM
DDDLIN==020000,,			; DD
PTYLIN==004000,,			; PTY
IMPBIT==001000,,			; IMP TTY
SPCBRK==000100,,			; special activation mode
BSACT== 000020				; activate on backspace

; Include wonderful network routines

NIORTS==-1				; include I/O routines
ERRHAN==-1				; include automagic error handling
ERRINS==IF1,[0] .ELSE JRST TOPLEV	; error instruction
ERRTNS==-1				; include error routines
HSTTAB==-1				; include host table routines

.INSRT NETWRK[NET,MRC]
SUBTTL Data area

CORBEG==.

FSPBLK:	BLOCK 4				; filespec block
INPFLN:	BLOCK 1				; input filename stuff
INPEXT:	BLOCK 1
INPPPN:	BLOCK 1
OUTFLN:	BLOCK 1				; output filename stuff
OUTEXT:	BLOCK 1
OUTPPN:	BLOCK 1

; Flags

TTINTP:	BLOCK 1				; -1 → TTI interrupt
NTINTP:	BLOCK 1				; -1 → NTI interrupt
NTOINP:	BLOCK 1				; ≤ -1 → output should be flushed
CLSINP:	BLOCK 1				; -1 → connection closing
NTBFOP:	BLOCK 1				; -1 → something in net buffer
NETCMP:	BLOCK 1				; -1 → network command in progress
INPFLP:	BLOCK 1				; -1 → input file opened
SLOWFP:	BLOCK 1				; -1 → input in slow mode
OUTFLP:	BLOCK 1				; -1 → output file opened
CHARMP:	BLOCK 1				; -1 → in character mode

IRPS OPT,,WILL WONT DO DONT
 OPT!P:	BLOCK 1				; -1 → option in effect
TERMIN

; Connection state flags

ECHOP:	BLOCK 1				; -1 → remote echoing
SUPGAP:	BLOCK 1				; -1 → suppressing GA

ECHORP:	BLOCK 1				; -1 → sent echo request

; Buffer headers

DSIBF:	BLOCK 3				; disk input buffer
DSOBF:	BLOCK 3				; disk output buffer

; TTY buffer stuff

TTOBFR:	BLOCK TTOBFL			; TTY output buffer
TTOCTR:	BLOCK 1				; TTY output counter
TTOPTR:	BLOCK 1				; TTY output pointer

COREND==.-1

; Stuff set in once only code and protected for restart zapping.

; Other buffers

PDL:	BLOCK PDLLEN			; pushdown list
HSPBUF:	BLOCK HSTNLN			; host argument stored here
HNMBUF:	BLOCK HSTNLN			; host name stored here

; Other flags

DEBUGP:	BLOCK 1				; -1 → MRC is fooling around
DPYP:	BLOCK 1				; -1 → display terminal
PTYP:	BLOCK 1				; -1 → on a PTY
MONCMP:	BLOCK 1				; -1 → monitor command

ESCHAR:	↑↑				; escape character for printing consoles
SUBTTL TELNET protocol codes

DEFINE TPC CODE
 CODE
 IRPS NAME,,CODE
  [ASCIZ/NAME/]
 .ISTOP
 TERMIN
TERMIN

; Top level codes

TPLTAB:

TPC SE==240.				; subnegotiation end
TPC NOP==241.				; no-op
TPC DM==242.				; data mark
TPC BRK==243.				; break key
TPC IP==244.				; interrupt process
TPC AO==245.				; abort output
TPC AYT==246.				; are you there?
TPC EC==247.				; erase character
TPC EL==248.				; erase line
TPC GA==249.				; go ahead
TPC SB==250.				; subnegotiation
TPC WILL==251.				; sender will do
TPC WONT==252.				; sender won't do
TPC DO==253.				; receiver asked to do
TPC DONT==254.				; receiver must not do
TPC IAC==255.				; interpret as command

TPLMIN==400-<.-TPLTAB>

; Various WILL/WONT/DO/DONT options

WDOTAB:

TPC TRNBIN==0.				; transmit binary
TPC ECHO==1.				; echo
TPC RCP==2.				; reconnect
TPC SUPRGA==3.				; suppress GA
TPC NAMS==4.				; negotiate approx. message size
TPC STATUS==5.				; status option
TPC TIMMRK==6.				; timing mark
TPC RCTE==7.				; remote controlled trans/echo
TPC NAOL==8.				; negotiate output line width
TPC NAOP==9.				; negotiate page size
TPC NAOCRD==10.				; negotiate output CR
TPC NAOHTS==11.				; negotiate output horizontal tab stops
TPC NAOHTD==12.				; negotiate output HT
TPC NAOFFD==13.				; negotiate output FF
TPC NAOVTS==14.				; negotiate output vertical tab stops
TPC NAOVTD==15.				; negotiate output VT
TPC NAOLFD==16.				; negotiate output LF
TPC EXTASC==17.				; Tovar's idea of extended ASCII
TPC LOGOUT==18.				; logout option
TPC BM==19.				; byte macro
TPC DET==20.				; data entry terminal option
TPC SUPDUP==21.				; SUPDUP (not TELNET) protocol
WDOMAX==.-WDOTAB-1

TPC EXOPL==255.				; extended options
SUBTTL Interrupt server

;  Interrupts only set flags which the main program (normally in INTW⊗
; state) looks at.  Clock interrupts fake the world since it is possible
; to lose an interrupt otherwise.  A network status change interrupt
; fakes an input interrupt so that everything typed ahead still is read.

INTSER:	SKIPN I,JOBCNI			; get interrupt status
	 FATAL Null interrupt
	TLNE I,(INTCLK)			; clock int fakes TTI and NTI
	 TLO I,(INTTTY\INTINP)
	TLNE I,(INTTTY)			; TTI int
	 SETOM TTINTP
	TLNE I,(INTINP)			; NTI int
	 SETOM NTINTP
	TLNE I,(INTIMS)			; status change
	 SETOM CLSINP
	TLNE I,(INTINS)			; IMP INS int
	 SOSL NTOINP
	  DISMIS			; dismiss interrupt

; Network interrupt, abort all TTY output!

	CLRBFO				; flush output in progress
	MOVEI I,5*TTOBFL-1		; reset TTY buffer counter
	MOVEM I,TTOCTR
	MOVE I,[440700,,TTOBFR]		; reset TTY buffer pointer
	MOVEM I,TTOPTR
	SETZM I,TTOBFR			; and zap buffer while at it
	MOVE I,[TTOBFR,,TTOBFR+1]
	BLT I,TTOBFR+TTOBFL-1
	DISMIS				; dismiss interrupt
SUBTTL Start of program

TELNET:	JFCL
	RESET
	SETZM MONCMP

; Scan monitor command line.

	RESCAN X
	JUMPLE X,CHKTTY			; no command to scan
	INCHRS X
	 JRST CHKTTY			; goddam bagbiting lying monitor
	TRZ X,"a#"A			; uppercaseify if necesary
	CAIE X,"N
	 CAIN X,"T
	  SKIPA Y,[" ]			; TELNET or NTELNET command, scan for space
	   MOVEI Y,";			; some other command, use semicolon
SCNARG:	INCHRS X
	 JRST CHKTTY
	CAIE X,↑J
	 CAIN X,175
	  JRST CHKTTY			; end of command line
	CAIE X,(Y)
	 JRST SCNARG
	SETOM MONCMP

; Gobble down host name from monitor command here

	SETZM HSPBUF
	MOVE [HSPBUF,,HSPBUF+1]
	BLT HSPBUF+HSTNLN-1
	MOVE Y,[440700,,HSPBUF]
	MOVEI Z,5*HSTNLN
SCNAR1:	INCHWL X
	CAIN X,↑M
	 JRST SCNAR1
	CAIE X,↑J
	 CAIN X,175
	  JRST CHKTTY
	IDPB X,Y			; save character in buffer
	SOJG Z,SCNAR1
SCNAR2:	INCHWL X			; flush extra characters
	CAIE X,↑J
	 CAIN X,175
	  JRST CHKTTY
	JRST SCNAR2			; what a loser

; Paw over terminal characteristics

CHKTTY:	SETZM DPYP
	HRROI [003000,,Y]
	TTYSET				; get line characteristics
	CAMN Y,[-1]
	 EXIT				; how can I work if detached?
	TLNE Y,(DISLIN\DMLIN\DDDLIN)	; display?
	 SETOM DPYP
	TLNE Y,(PTYLIN)
	 SETOM PTYP
; (continued on next page)
; Check for net-hoppers.

	TLNE Y,(IMPBIT)			; check for net hoppers
	 JRST NETHOP
	SKIPN MONCMP
	 JRST GETHST			; not command; prompt for host
	JRST GETHS1			; no host prompt

; This loser is net hopping!

NETHOP:	OUTSTR [ASCIZ/Foo you are a net hopper.
/]
	SKIPN MONCMP
	 JRST GETHST			; no further hassle if not command
	OUTSTR [ASCIZ/
 You are logged into SAIL over the ARPAnet.  It is a waste of SAIL's
limited system resources (jobs, network links, etc.) to go back again
over the same network.  It also greatly slows down response to you
and increases the chances of lossage due to a system or network failure.

 You should not do this unless you have a good reason to do so.  If you
have any questions, please contact MRC and LES for more information.
Thank you for your co-operation.

 Are you SURE you want to TELNET now?/]
	INCHRW
	ANDI 137
	CAIE "Y
	 EXIT
	OUTSTR [ASCIZ/
/]
	JRST GETHS1			; continue monitor command
SUBTTL Top level

TOPLEV:	SKIPE MONCMP			; called from monitor level?
	 JRST BYEBYE			; yes, bye bye

GETHST:	OUTSTR [ASCIZ/Host = /]

; Set up the world

GETHS1:	RESET				; clear all I/O
	MOVE JOBFF
	CORE				; smallify
	 JFCL
	SETZM CORBEG
	MOVE [CORBEG,,CORBEG+1]
	BLT COREND			; zak!
	MOVE P,[PDL(-PDLLEN)]		; set up stack pointer
	OPEN DSI,[0 ? 'DSK,, ? DSIBF]	; get a disk input channel
	 FATAL DSK OPEN failed
	OPEN DSO,[0 ? 'DSK,, ? DSOBF,,]	; get a disk output channel
	 FATAL DSK OPEN failed
	SETACT [[	777777,,777777	; activate on everything
			777777,,777777	; just set it up for when we need it
			777777,,777777
			777777,,600000\BSACT]]
	SETZM HOST
	MOVEI NPRSKT
	MOVEM ICPSKT

; Now preprocess the host name

	SKIPE MONCMP
	 JRST GOTHST			; already set up
	SETZM HSPBUF
	MOVE [HSPBUF,,HSPBUF+1]
	BLT HSPBUF+HSTNLN-1
	MOVE Y,[440700,,HSPBUF]
	MOVEI Z,5*HSTNLN
GETHCH:	INCHWL X
	CAIN X,775			; αβALT is magic
	 PUSHJ P,DDTCAL
	ANDI X,177
	CAIN X,↑M
	 JRST GETHCH
	CAIE X,↑J
	 CAIN X,175
	  JRST GOTHST
	IDPB X,Y			; save character in buffer
	SOJG Z,GETHCH
FLSHEX:	INCHWL X			; flush extra characters
	CAIN X,775			; αβALT is magic
	 PUSHJ P,DDTCAL
	ANDI X,177
	CAIE X,↑J
	 CAIN X,175
	  JRST GOTHST
	JRST FLSHEX			; what a loser
SUBTTL Process host specification

GOTHST:	MOVE HSPBUF
	ANDCM [<ASCII/XXX/>#<ASCII/xxx/>]; convert cases
	CAME [ASCII/NSW/]		; happy Geoff
	 JRST NOTNSW
	MOVE [ASCII/33@SR/]
	MOVEM HSPBUF
	MOVE [ASCII/I-KA/]
	MOVEM HSPBUF+1
NOTNSW:	MOVE Y,[440700,,HSPBUF]
	MOVE Z,[440700,,HNMBUF]
	ILDB X,Y			; first character tells it all
	JUMPE X,TOPLEV
	CAIL X,"0
	 CAIL X,"9
	  JRST ALPHST			; alphabetic host specification
	PUSHJ P,SWINIR			; get socket or host number
	JUMPE X,GOTHSN			; end of spec, host number
	SKIPL A				; octal has priority over decimal
	 MOVE B,A
	JUMPLE B,[	OUTSTR [ASCIZ/Illegal socket number
/]
			CLRBFI
			JRST TOPLEV]
	MOVEM B,ICPSKT
	CAIE X,",
	 CAIN X,"@
	  JRST HSTSPC
	OUTSTR [ASCIZ/Illegal character in socket number
/]
	CLRBFI
	JRST TOPLEV

; Host specification

HSTSPC:	ILDB X,Y			; first character must be numeric
	JUMPE X,HSTLUZ
	CAIL X,"0
	 CAIL X,"9
	  JRST ALPHST
	PUSHJ P,SWINIR			; get socket
GOTHSN:	SKIPL A
	 MOVE B,A
	JUMPLE B,[	OUTSTR [ASCIZ/Illegal host number

/]
			CLRBFI
			JRST TOPLEV]
	MOVEM B,HOST
	JUMPE X,GOICP			; end of spec
HSTLUZ:	OUTSTR [ASCIZ/Illegal character in host number
/]
	CLRBFI
	JRST TOPLEV

; Alphabetic host specification

ALPHST:	IDPB X,Z			; copy spec into block for HSTNAM
	JUMPE X,CHKHNM
	ILDB X,Y
	JRST ALPHST
; Host name specified, ask magical routine to find it

CHKHNM:	PUSHJ P,MAPHST			; bring host table in core
	SKIPE HOST			; host name waiting?
	 JRST [	PUSHJ P,HSTNUM		; no, just try and get an HDB
		 TDZA 2,2		; no HDB, set up as nothing
		  JRST GOTHDB		; won with HDB
		MOVEI 1,[ASCIZ/RANDOM-PLACE/]
		JRST GOTHDB]		; continue with fake HDB
	MOVEI HNMBUF
	PUSHJ P,HSTNAM			; get descriptor block for the host
	 JRST [	OUTSTR [ASCIZ/No such host.
/]
		PUSHJ P,UNMHST
		CLRBFI
		JRST TOPLEV]
	 JRST [	OUTSTR [ASCIZ/Ambiguous host name.
/]
		PUSHJ P,UNMHST
		CLRBFI
		JRST TOPLEV]
	MOVEM HOST
GOTHDB:	TLNN 1,-1			; any system spec?
	 JRST NOSYS			; unknown system
	HLRZ X,1
	MOVE X,(X)
	CAME X,[ASCII/ITS/]		; if an ITS,
NOSYS:	 SKIPN DPYP			; or not a display
	  PUSHJ P,ECHATM		; use character mode
	PUSHJ P,UNMHST			; flush host table
SUBTTL ICP ICP ICP

GOICP:	PTJOBX [0 ? 3]			; local echo off
	OUTSTR [ASCIZ/ Trying... /]
	PUSH P,ICPSKT			; for check below
	PUSHJ P,CONECT			; call wonderful ICPer
	OUTSTR [ASCIZ/Open
/]

; Initialize interrupts

	MOVEI INTSER
	MOVEM JOBAPR			; set up interrupt server
	CLKINT 60.*CLKSPD		; start the ticking clock
	MOVSI (INTTTY\INTCLK\INTINS\INTIMS\INTINP)
	INTENB				; enable interrupts
	LOCK				; prevent swapouts

;  If new protocol, flush cretin GA's (we refuse to implement 'em) and try to
; get local echoing.

	POP P,				; get ICP socket we used
	CAIE NPRSKT			; new protocol?
	 JRST GOICP1			; may be an FTP or something
	SNEAKS
	 JRST GOICP0
	CAIN 700			; if αβ@ typed ahead
	 SETOM DEBUGP			; MRC is fooling around!
GOICP0:	TELCMD [IAC DO ECHO IAC DO SUPRGA]
	SETOM ECHORP ? SETOM SUPGAP

; Initialize TTY output buffer variables and randomness

GOICP1:	MOVEI 5*TTOBFL-1		; set up TTY buffer counter
	MOVEM TTOCTR
	MOVE [440700,,TTOBFR]		; set up TTY buffer pointer
	MOVEM TTOPTR
	SETZM TTOBFR
	MOVE [TTOBFR,,TTOBFR+1]
	BLT TTOBFR+TTOBFL-1
	INSKIP
	 JRST SLEEPR
	SETOM TTINTP

; (continued on next page)
SUBTTL Main program loop

SLEEPR:	SKIPL INPFLP			; unless input file open,
	 IWAIT				; sleep for an interrupt
SLEPR1:	AOSG TTINTP			; TTY int?
	 JRST TTISER
	SKIPN CLSINP			; if closing, keep trying input till lossage
	 AOSG NTINTP			; NTI int?
	  JRST NTISER
	SKIPL INPFLP			; input file open?
	 JRST SLEEPR
GETDCH:	SOSG DSIBF+2
	 IN DSI,
	  CAIA
	   JRST [	CLOSE DSI,
			PUSHJ P,NETSND
			OUTSTR [ASCIZ/End of input file /]
			MOVE X,INPFLN
			PUSHJ P,OUTSIX
			OUTCHR [".]
			MOVE X,INPEXT
			PUSHJ P,OUTSIX
			OUTCHR ["[]	;]
			HLLZ X,INPPPN
			PUSHJ P,OUTSIX
			OUTCHR [",]
			HRLZ X,INPPPN
			PUSHJ P,OUTSIX
			OUTSTR [ASCIZ/].
/]
			SETZM INPFLP
			JRST SLEEPR]
	ILDB DSIBF+1
	JUMPE GETDCH
	SKIPE SLOWFP			; nice slow file processing?
	 JRST CHRHAK			; yah, force on every character

; Duplicate of TTYSER's CHRHAK to avoid a force on each character

	SKIPN ECHOP			; echo if in local mode
	 OUTCHR				; (this way avoids command echoing)

; Canonicalize from SAIL to standard ASCII

	CAIN 175			; ALT
	 MOVEI 33
	CAIN 176			; }
	 MOVEI 175
	CAIN 32				; ~
	 MOVEI 176

; Here to actually send the character

	PUSHJ P,NETOCH			; output the character
	JRST SLEPR1
; TTY input interrupt

TTISER:	INCHSL				; get a character
	 JRST [	AOSG NTBFOP		; anything in the buffer?
		 PUSHJ P,NETSND		; force it out
		AOSG NTINTP		; TTI buffer empty
		 JRST NTISER		; but some net stuff to handle
		JRST SLEEPR]

;  Command and mapping stuff.  We only map between our character set and
; ASCII.  Anybody who wants mapping to MIT's character set should use SUPDUP!!

	CAIN 612			; αβLF?
	 JRST [	MOVEI ↑Z		; probably ↑Z on a loser
		SKIPN PTYP
		 JRST TTISR1		; whatever
		JRST CHRHAK]		; on a PTY it is ~!!!
	SKIPN DPYP			; no ↑↑ processing if a display
	 JRST [	CAME ESCHAR
		 JRST .+1		; not escape character
		INCHRW
		CAMN ESCHAR		; meta?
		 JRST [	INCHRW
			CAMN ESCHAR	; control-meta?
			 JRST [	INCHRW
				IORI 600; form αβcharacter
				JRST .+1]
			IORI 400	; form βcharacter
			JRST .+1]
		IORI 200		; form αcharacter
		JRST .+1]
	CAIN 775			; αβALT is magic
	 PUSHJ P,DDTCAL
	CAIN 777			; αβBS?
	 JRST [	MOVEI 177 ? JRST TTISR1]; just an ordinary character
	TRZE 400			; META set?
	 JRST [	LDB X,[000700,,0]	; get ASCII part
		CAILE X,"←
		 SUBI X,"a-"A		; uppercaseify if necessary
		SUBI X,"@
		JUMPL X,NTISER		; no op character
		TRNN 200		; CONTROL?
		 SKIPA X,CMCDSP(X)	; no, use right half
		  HLR X,CMCDSP(X)	; yes, use left half
		PUSHJ P,(X)
		JRST TTISER]
	TRZE 200			; if CONTROL is set
	 JRST [	TRZ 140			; convert to canonical ASCII control
		JRST TTISR1]

; Here only if an ASCII printing character

CHRHAK:	SKIPN ECHOP			; echo if in local mode
	 OUTCHR				; (this way avoids command echoing)

; Canonicalize from SAIL to standard ASCII

	CAIN 175			; ALT
	 MOVEI 33
	CAIN 176			; }
	 MOVEI 175
	CAIN 32				; ~
	 MOVEI 176

; Here to actually send the character

TTISR1:	PUSHJ P,NETOCH			; output the character
	SETOM NTBFOP			; flag there is network output
	JRST TTISER
; Network input interrupt

NTISER:	SKIPE CLSINP			; closing?
	 JRST [	PUSHJ P,NETICW		; slurp slurp slurp
		JRST NTISR2]
	AOSG TTINTP
	 JRST [	SETOM NTINTP		; make sure we come back here
		JRST TTISER]		; give the TTY a chance!
	PUSHJ P,NETICH			; get a character
	 JRST [	SKIPL NTOINP		; output reset in progress?
		 OUTSTR TTOBFR
		MOVEI 5*TTOBFL-1	; reset TTY buffer counter
		MOVEM TTOCTR
		MOVE [440700,,TTOBFR]	; reset TTY buffer pointer
		MOVEM TTOPTR
		SETZM TTOBFR
		MOVE [TTOBFR,,TTOBFR+1]
		BLT TTOBFR+TTOBFL-1
		AOSG TTINTP
		 JRST TTISER		; TTI int to be taken care of
		JRST SLEEPR]		; else sleep
NTISR2:	AOSG NETCMP			; IAC in progress?
	 JRST IACSER
	IRPS OPT,,WILL WONT DO DONT
	 AOSG OPT!P
	  JRST OPT!SR
	TERMIN
	CAIN IAC			; network command?
	 JRST [	SETOM NETCMP		; remember that one is coming
		JRST NTISER]
NTISR1:	JUMPE NTISER			; flush nulls
	CAIN 176			; ~
	 MOVEI 32
	CAIN 175			; }
	 MOVEI 176
	CAIN 33				; diamond
	 MOVEI 175
	CAIN ↑G
	 JRST [	HRROI -1
		BEEP
		JRST NTISER]		; map bells to bells
	CAIN 177			; rubout is usually padding
	 JRST NTISER
	SOSG TTOCTR			; buffer stuffed?
	 JRST [	SKIPL NTOINP		; output reset?
		 OUTSTR TTOBFR
		MOVEI X,5*TTOBFL-1	; set up TTY buffer counter
		MOVEM X,TTOCTR
		MOVE X,[440700,,TTOBFR]	; set up TTY buffer pointer
		MOVEM X,TTOPTR
		SETZM TTOBFR
		MOVE X,[TTOBFR,,TTOBFR+1]
		BLT X,TTOBFR+TTOBFL-1
		JRST .+1]
	IDPB TTOPTR
	SKIPL OUTFLP			; output file in progress?
	 JRST NTISER
	SOSG DSOBF+2
	 OUTPUT DSO,
	IDPB DSOBF+1
	JRST NTISER
SUBTTL IAC service

IACSER:	CAIN IAC			; quoted IAC?
	 JRST NTISR1			; just send it
	SKIPE DEBUGP
	 PUSHJ P,TPLMSG
	CAIN DM				; data mark?
	 JRST [	AOSE NTOINP
		 JRST NTISER		; no output reset in progress
		MOVEI I,5*TTOBFL-1	; end of an output reset
		MOVEM I,TTOCTR
		MOVE I,[440700,,TTOBFR]	; reset TTY buffer pointer
		MOVEM I,TTOPTR
		SETZM I,TTOBFR		; and zap buffer while at it
		MOVE I,[TTOBFR,,TTOBFR+1]
		BLT I,TTOBFR+TTOBFL-1
		JRST NTISER]
	IRPS OPT,,WILL WONT DO DONT
	 CAIN OPT
	  JRST [SETOM OPT!P
		JRST NTISER]
	TERMIN
	CAIN SB
	 WARN Foreign host sent a subnegotiation
	JRST NTISER			; not an option I know

; Protocol command message for MRC's fooling around

TPLMSG:	OUTSTR [ASCIZ/*IAC /]
	CAIGE TPLMIN			; big enough?
	 JRST @RNDMSG
	MOVE 1,
	OUTSTR @TPLTAB-TPLMIN(1)
	CAIGE WILL
	 OUTSTR [ASCIZ/*
/]
	POPJ P,

; WILL/WONT/DO/DONT option message for MRC's fooling around

OPTMSG:	CAIN EXOPL
	 JRST [	OUTSTR [ASCIZ/ EXOPL*
/]
		POPJ P,]
	OUTCHR [" ]
	CAILE WDOMAX
RNDMSG:	 JRST [	IDIVI 100
		ADDI "0
		OUTCHR
		IDIVI 10
		ADDI 1,"0
		OUTCHR 1
		ADDI 2,"0
		OUTCHR 2
		OUTSTR [ASCIZ/*
/]
		POPJ P,]
	MOVE 1,
	OUTSTR @WDOTAB(1)
	OUTSTR [ASCIZ/*
/]
	POPJ P,
; IAC WILL/WONT

WILLSR:	SKIPE DEBUGP
	 PUSHJ P,OPTMSG
	CAIN ECHO			; remote option (what a win!)
	 JRST [	SKIPE ECHOP		; catch protocol violators
		 JRST [	SETZM ECHORP ? JRST NTISER]
		SETOM ECHOP
		AOSN ECHORP		; command or reply?
		 JRST NTISER
		TELCMD [IAC DO ECHO]
		JRST NTISER]		; command, we always accept it
	CAIN SUPRGA			; suppress GA?
	 JRST [	SKIPE SUPGAP		; command or reply?
		 JRST NTISER
		SETOM SUPGAP
		TELCMD [IAC DO SUPRGA]
		JRST NTISER]

; Not an option we like, refuse it

	PUSH P,
	SKIPE DEBUGP
	 OUTSTR [ASCIZ/⊗IAC DONT/]
	MOVEI IAC
	PUSHJ P,NETOCH
	MOVEI DONT
	PUSHJ P,NETOCH
	POP P,
	SKIPE DEBUGP
	 PUSHJ P,OPTMSG
	PUSHJ P,NETOCH
	PUSHJ P,NETSND
	JRST NTISER

WONTSR:	SKIPE DEBUGP
	 PUSHJ P,OPTMSG
	CAIN ECHO			; local echo?
	 JRST [	SKIPN ECHOP
		 JRST [	SETZM ECHORP ? JRST NTISER]
		SETZM ECHOP		; back to lossage
		AOSN ECHORP		; need to confirm?
		 JRST NTISER
		TELCMD [IAC DONT ECHO]
		JRST NTISER]
	CAIE SUPRGA
	 SKIPL SUPGAP
	  JRST NTISER			; protocol violator
	SETZM SUPGAP
	TELCMD [IAC DONT SUPRGA]
	JRST NTISER			; loser
; IAC DO/DONT

DOSR:	SKIPE DEBUGP
	 PUSHJ P,OPTMSG
	CAIN TIMMRK			; silly Multix and Tenex cretinism?
	 JRST [	TELCMD [IAC WILL TIMMRK]
		JRST NTISER]		; yes, make the losers happy

; Not an option we like, refuse it

	PUSH P,
	SKIPE DEBUGP
	 OUTSTR [ASCIZ/⊗IAC WONT/]
	MOVEI IAC
	PUSHJ P,NETOCH
	MOVEI WONT
	PUSHJ P,NETOCH
	POP P,
	SKIPE DEBUGP
	 PUSHJ P,OPTMSG
	PUSHJ P,NETOCH
	PUSHJ P,NETSND
	JRST NTISER

; If we get a DONT the loser is violating protocol!!

DONTSR:	SKIPE DEBUGP
	 PUSHJ P,OPTMSG
	JRST NTISER
SUBTTL Character commands

; Command dispatch table

CMCDSP:	REPEAT 40,[NTISER,,NTISER ? ]	; default to no-op

DEFINE CMDCHR CHR,CDISP,DISP
 LOC CMCDSP+"CHR-"@
 CDISP,,DISP
TERMIN

; Command dispatch table.  All routines are assumed to return via POPJ P,

; CMDCHR character,αβdispatch,βdispatch

CMDCHR @,DBUG,NDBUG			; MRC fooling around
CMDCHR A,ATTN,ATTN			; send ATTN
CMDCHR B,BREAK,BREAK			; send BRK
CMDCHR C,CLSCON,CLSCON			; close connection
CMDCHR D,CLSOFL,OPNOFL			; output file
CMDCHR E,RECHO,LECHO			; echo mode
CMDCHR F,APPEND,DAPPND			; append file
CMDCHR I,CLSIFL,OPNIFL			; input file
CMDCHR J,EOFF,EON			; echo diddle without telling host
CMDCHR L,ECHATM,LCHATM			; line editor diddle
CMDCHR O,ABORTO,ABORTO			; abort output
CMDCHR Q,PUNT,PUNT			; exit
CMDCHR R,CLSIFL,OPNIFS			; open file in nice slow way
CMDCHR W,RUTHER,RUTHER			; are you there?
CMDCHR X,ESCSET,ESCSET			; set escape character

LOC CMCDSP+40
SUBTTL Command service routines

; MRC fooling around

DBUG:	SETOM DEBUGP ? POPJ P,
NDBUG:	SETZM DEBUGP ? POPJ P,

; Send ATTN

ATTN:	PUSHJ P,NETINS			; send INS
	TELCMD [IAC IP IAC DM]		; and data mark
	POPJ P,

; Send BRK

BREAK:	PUSHJ P,NETINS			; send INS
	TELCMD [IAC BRK IAC DM]
	POPJ P,

; Close connection

CLSCON:	PUSHJ P,CLOSER
	SETZM MONCMP			; forget being a monitor command
	JRST TOPLEV

; Stop everything...

PUNT:	PUSHJ P,CLOSER			; close connections
BYEBYE:	UNLOCK
	EXIT

; Status

RUTHER:	TELCMD [IAC AYT]
	POPJ P,

; Set escape character

ESCSET:	INCHRW ? MOVEM ESCHAR ? POPJ P,
; Append file hacking

; Append to a file and always ask

APPEND:	SKIPGE OUTFLP			; file open?
	 JRST [	OUTSTR [ASCIZ/Output file already open!
/]
		POPJ P,]
	OUTSTR [ASCIZ/Append file name: /]
	HRROI [002000,,(SPCBRK)]
	SKIPE CHARMP
	 TTYSET				; leave special activation mode
	PTJOBX [0 ? 4]			; echo filespec
	PUSHJ P,GETFSP			; get filespec
	PTJOBX [0 ? 3]
	HRROI [001000,,(SPCBRK)]
	SKIPE CHARMP
	 TTYSET				; enter special activation mode
	MOVE FSPBLK ? MOVEM OUTFLN
	MOVE FSPBLK+1 ? MOVEM OUTEXT
	MOVE FSPBLK+3 ? MOVEM OUTPPN
	LOOKUP DSO,FSPBLK
	 JRST [	OUTSTR [ASCIZ/LOOKUP failed!
/]
		SETZM OUTFLN		; toss away default
		POPJ P,]
	MOVE X,OUTPPN
	MOVEM X,FSPBLK+3
	ENTER DSO,FSPBLK
	 JRST [	OUTSTR [ASCIZ/ENTER failed!
/]
		POPJ P,]
	UGETF DSO,			; start appending
	SETOM OUTFLP
	POPJ P,

; Append but try using defaults

DAPPND:	SKIPGE OUTFLP			; file open?
	 JRST [	OUTSTR [ASCIZ/Output file already open!
/]
		POPJ P,]
	SKIPN X,OUTFLN
	 JRST APPEND
	MOVEM X,FSPBLK
	MOVE X,OUTEXT
	MOVEM X,FSPBLK+1
	SETZM FSPBLK+2
	MOVE X,OUTPPN
	MOVEM X,FSPBLK+3
	LOOKUP DSO,FSPBLK
	 JRST [	OUTSTR [ASCIZ/LOOKUP failed!
/]
		SETZM OUTFLN		; toss away default
		POPJ P,]
	MOVE X,OUTPPN
	MOVEM X,FSPBLK+3
	ENTER DSO,FSPBLK
	 JRST [	OUTSTR [ASCIZ/ENTER failed!
/]
		POPJ P,]
	UGETF DSO,			; start appending
	SETOM OUTFLP
	OUTSTR [ASCIZ/Appending to file /]
	MOVE X,OUTFLN
	PUSHJ P,OUTSIX
	OUTCHR [".]
	MOVE X,OUTEXT
	PUSHJ P,OUTSIX
	OUTCHR ["[]	;]
	HLLZ X,OUTPPN
	PUSHJ P,OUTSIX
	OUTCHR [",]
	HRLZ X,OUTPPN
	PUSHJ P,OUTSIX
	OUTSTR [ASCIZ/]
/]
	POPJ P,
; Output file hacking

; Close output file

CLSOFL:	AOSE OUTFLP			; file open?
	 POPJ P,
	CLOSE DSO,			; close output
	OUTSTR [ASCIZ/Output file /]
	MOVE X,OUTFLN
	PUSHJ P,OUTSIX
	OUTCHR [".]
	MOVE X,OUTEXT
	PUSHJ P,OUTSIX
	OUTCHR ["[]	;]
	HLLZ X,OUTPPN
	PUSHJ P,OUTSIX
	OUTCHR [",]
	HRLZ X,OUTPPN
	PUSHJ P,OUTSIX
	OUTSTR [ASCIZ/] closed.
/]
	POPJ P,

; Open output file

OPNOFL:	SKIPGE OUTFLP			; file open?
	 JRST [	OUTSTR [ASCIZ/Output file already open!
/]
		POPJ P,]
	OUTSTR [ASCIZ/Output file name: /]
	HRROI [002000,,(SPCBRK)]
	SKIPE CHARMP
	 TTYSET				; leave special activation mode
	PTJOBX [0 ? 4]			; echo filespec
	PUSHJ P,GETFSP			; get filespec
	PTJOBX [0 ? 3]
	HRROI [001000,,(SPCBRK)]
	SKIPE CHARMP
	 TTYSET				; enter special activation mode
	MOVE FSPBLK ? MOVEM OUTFLN
	MOVE FSPBLK+1 ? MOVEM OUTEXT
	MOVE FSPBLK+3 ? MOVEM OUTPPN
	ENTER DSO,FSPBLK
	 JRST [	OUTSTR [ASCIZ/ENTER failed!
/]
		POPJ P,]
	SETOM OUTFLP
	POPJ P,
; Input file hacking

; Close input file

CLSIFL:	AOSE INPFLP			; file open?
	 POPJ P,
	CLOSE DSI,			; close input
	OUTSTR [ASCIZ/Input file /]
	MOVE X,INPFLN
	PUSHJ P,OUTSIX
	OUTCHR [".]
	MOVE X,INPEXT
	PUSHJ P,OUTSIX
	OUTCHR ["[]	;]
	HLLZ X,INPPPN
	PUSHJ P,OUTSIX
	OUTCHR [",]
	HRLZ X,INPPPN
	PUSHJ P,OUTSIX
	OUTSTR [ASCIZ/] closed.
/]
	SETZM SLOWFP
	POPJ P,

; Open input file

OPNIFS:	SETOM SLOWFP
OPNIFL:	SKIPGE INPFLP			; file open?
	 JRST [	OUTSTR [ASCIZ/Input file already open!
/]
		POPJ P,]
	OUTSTR [ASCIZ/Input file name: /]
	HRROI [002000,,(SPCBRK)]
	SKIPE CHARMP
	 TTYSET				; leave special activation mode
	PTJOBX [0 ? 4]			; echo filespec
	PUSHJ P,GETFSP			; get filespec
	PTJOBX [0 ? 3]
	HRROI [001000,,(SPCBRK)]
	SKIPE CHARMP
	 TTYSET				; enter special activation mode
	MOVE FSPBLK ? MOVEM INPFLN
	MOVE FSPBLK+1 ? MOVEM INPEXT
	MOVE FSPBLK+3 ? MOVEM INPPPN
	LOOKUP DSI,FSPBLK
	 JRST [	OUTSTR [ASCIZ/LOOKUP failed!
/]
		SETZM SLOWFP
		POPJ P,]
	SETOM INPFLP
	POPJ P,
; More commands

; Enter remote echo mode

RECHO:	SKIPE ECHOP
	 POPJ P,
	SETOM ECHORP
	TELCMD [IAC DO ECHO]
	POPJ P,

; Enter local echo mode

LECHO:	SKIPN ECHOP
	 POPJ P,
	SETOM ECHORP
	TELCMD [IAC DONT ECHO]
	POPJ P,

; Echo diddle without asking host

EOFF:	SETOM ECHOP ? POPJ P,
EON:	SETZM ECHOP ? POPJ P,

; Enter character-at-a-time mode

ECHATM:	SETOM CHARMP
	HRROI [001000,,(SPCBRK)]
	TTYSET				; enter special activation mode
	POPJ P,

; Leave character-at-a-time mode

LCHATM:	SETZM CHARMP
	HRROI [002000,,(SPCBRK)]
	TTYSET				; leave special activation mode
	POPJ P,

; Abort output

ABORTO:	CLRBFO
	PUSHJ P,NETINR			; send an INR
	TELCMD [IAC AO IAC DM]
	POPJ P,
SUBTTL Random subroutines

; Call DDT if there is a DDT present

DDTCAL:	SKIPN JOBDDT
	 POPJ P,			; no DDT!
	OUTSTR [ASCIZ/You're in DDT.
/]
	JRST @JOBDDT			; enter DDT


; Filespec input routine.  Smashes X, Y, and Z; sets up FSPBLK.

GETFSP:	SETZM FSPBLK ? SETZM FSPBLK+1 ? SETZM FSPBLK+2
	SETZ X,
	DSKPPN X,
	MOVEM X,FSPBLK+3
	PUSHJ P,GETSIX			; get file name
	JUMPE X,FSPLUZ
	MOVEM X,FSPBLK			; got file name
	CAIE Y,".
	 JRST NOEXT
	PUSHJ P,GETSIX			; try for extension
	MOVEM X,FSPBLK+1
NOEXT:	CAIN Y,↑J
	 POPJ P,
	CAIE Y,"[			; must be a PPN
	 JRST FSPLUZ
	PUSHJ P,GETSIX
	TRNE X,-1
	 JRST FSPLUZ
	TLNN X,77
	 JUMPN X,[LSH X,-6 ? JRST .-1]
	SKIPE X
	 HLLM X,FSPBLK+3
	CAIE Y,",
	 JRST FSPEOS
	PUSHJ P,GETSIX
	TRNE X,-1
	 JRST FSPLUZ
	TLNN X,77
	 JUMPN X,[LSH X,-6 ? JRST .-1]
	SKIPE X
	 HLRM X,FSPBLK+3
FSPEOS:	CAIN Y,"]
	 INCHWL Y
	CAIN Y,↑M
	 INCHWL Y
	CAIN Y,↑J
	 POPJ P,
FSPLUZ:	CLRBFI
	OUTSTR [ASCIZ/Invalid file specification.  Try again: /]
	JRST GETFSP
; More random subroutines

; Sixbit output routine.  Takes a word in X, smashes Y, flushes spaces.

OUTSIX:	SETZ Y,
	ROTC X,6
	JUMPE Y,OUTSX1
	ADDI Y,"A-'A
	OUTCHR Y
OUTSX1:	JUMPN X,OUTSIX
	POPJ P,


; Sixbit input routine.  Inputs a sixbit word in X, smashes Y and Z.

GETSIX:	SETZ X,
	MOVE Z,[440600,,X]
GETSX1:	INCHWL Y
	CAIN Y,↑M
	 INCHWL Y
	CAIL Y,"a			; convert to upper case
	 CAILE Y,"z
	  CAIA
	   SUBI Y,"a-"A
	CAIL Y,"0			; only allow alphanumerics
	 CAILE Y,"Z
	  POPJ P,
	CAILE Y,"9
	 CAIL Y,"A
	  CAIA
	   POPJ P,
	SUBI Y,"A-'A			; convert to sixbit
	TRNN X,77			; don't go beyond last byte
	 IDPB Y,Z
	JRST GETSX1
	 

;  Super winning numeric input routine.  Numbers are parsed as both octal and
; decimal, unless either (a) an 8 or 9 appears in the number, or (b) the number
; is followed by a decimal point.

SWINIR:	SETZB A,B			; A ← octal number, B ← decimal
SWINR1:	CAIL X,"8			; if can't be octal, A ← 1
	 SETO A,
	JUMPL A,SWINR2
	LSH A,3
	ADDI A,-"0(X)			; bring in next octal digit
SWINR2:	IMULI B,10.
	ADDI B,-"0(X)			; bring in next decimal digit
	ILDB X,Y
	CAIN X,".			; decimal point ends spec and forces decimal
	 JRST [	SETO A,
		ILDB X,Y
		POPJ P,]
	CAIL X,"0
	 CAIL X,"9
	  POPJ P,			; non-numeric, return
	JRST SWINR1


...LIT:	CONSTANTS

END TELNET